﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Terrain44
{

    public struct VertexExplosion
    {
        public Vector3 position;
        public Vector4 texcoord; //(texcoord.x, texcoord.y, birthtime, maxTime)
        public Vector4 additionalInfo1; //(randMoveDirection.x, randMoveDirection.y, randMoveDirection.z, randValue for size and speed of particle)
        public Vector4 additionalInfo2; //(objectDirection.x, objectDirection.y, objectDirection.z, total size of explosion)
        public Vector4 additionalInfo3; //(size of a single particle for the center of explosion, -free-,-free-,-free-)

        public VertexExplosion(Vector3 position, Vector4 texcoord, Vector4 additionalInfo1, Vector4 additionalInfo2, Vector4 additionalInfo3)
        {
            this.position = position;
            this.texcoord = texcoord;
            this.additionalInfo1 = additionalInfo1;
            this.additionalInfo2 = additionalInfo2;
            this.additionalInfo3 = additionalInfo3;
        }

        public static readonly int sizeInBytes = (3 + 4 + 4 + 4 + 4) * sizeof(float);

        public static readonly VertexDeclaration vertexDeclaration = new VertexDeclaration
            (new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
             new VertexElement(sizeof(float) * 3, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 0),
             new VertexElement(sizeof(float) * (3 + 4), VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 1),
             new VertexElement(sizeof(float) * (3 + 4 + 4), VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 2),
             new VertexElement(sizeof(float) *(3 + 4 + 4 + 4), VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 3)); 
    }

    class Explosion
    {
        Random rand;
        Vector3 startPos;
        int particleCount;
        float particleSize;
        float totalSize;
        Vector3 objectDirection;
        GraphicsDevice device;
        float birthTime;
        float maxTime;
        VertexExplosion[] explosionVertices;
        Effect expEffect;
        Texture2D expTexture;

        VertexBuffer deviceVertexBuffer;

        //camera????view world projection, camPos, camUp
        //moveDirection vector3

        Matrix view;
        Matrix projection; 
        Vector3 camPos;
        Vector3 camUp;

        public Explosion(Vector3 startPos, int particleCount, float particleSize, float totalSize, GraphicsDevice device, float birthTime, Vector3 objectDirection, Effect expEffect, Texture2D expTexture)
        {
            rand = new Random();
            this.startPos = startPos;
            this.particleCount = particleCount;
            this.particleSize = particleSize;
            this.totalSize = totalSize;
            this.device = device;
            this.birthTime = birthTime;
            this.expEffect = expEffect;
            this.expTexture = expTexture;
            this.objectDirection = objectDirection;

            maxTime = 100.0f;


            view = Matrix.CreateLookAt(new Vector3(0, 150, 80), new Vector3(0, 0, -10), new Vector3(0, 1, 0));
            projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 1.0f, 1000.0f);
            camPos = new Vector3(0, 150, 80);

            camUp = new Vector3(0, 1, 0);



            CreateExplosionVertices(birthTime);

        }

        //given the current time by the calling method
        private void CreateExplosionVertices(float birthTime)
        {
            //according to how many particles will be need, create an array that can hold
            //all vertices (6 vertices per particle, -> 2 triangles)
            int particles = particleCount;
            explosionVertices = new VertexExplosion[particles * 6];

            int i = 0;
            for (int partnr = 0; partnr < particles; partnr++)
            {
                //the starting position of each particle, the center of the explosion
                Vector3 startingPos = startPos;

                //random moving direction with equal length (normalize) of each particle
                float r1 = (float)rand.NextDouble() - 0.5f; //range [-0.5, 0.5]
                float r2 = (float)rand.NextDouble() - 0.5f;
                float r3 = (float)rand.NextDouble() - 0.5f;
                Vector3 moveDirection = new Vector3(r1, r2, r3);
                moveDirection.Normalize();

                //random value to adjust speed and size of the particle
                float r4 = (float)rand.NextDouble();
                r4 = (r4 / 4.0f) * 3.0f + 0.25f; //range [0.25, 1.0]

                //create the vertices for each particle
                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(1, 1, birthTime, maxTime), new Vector4(moveDirection, r4), new Vector4(objectDirection, totalSize), new Vector4(particleSize, 0, 0, 0));
                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(0, 0, birthTime, maxTime), new Vector4(moveDirection, r4), new Vector4(objectDirection, totalSize), new Vector4(particleSize, 0, 0, 0));
                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(1, 0, birthTime, maxTime), new Vector4(moveDirection, r4), new Vector4(objectDirection, totalSize), new Vector4(particleSize, 0, 0, 0));

                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(1, 1, birthTime, maxTime), new Vector4(moveDirection, r4), new Vector4(objectDirection, totalSize), new Vector4(particleSize, 0, 0, 0));
                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(0, 1, birthTime, maxTime), new Vector4(moveDirection, r4), new Vector4(objectDirection, totalSize), new Vector4(particleSize, 0, 0, 0));
                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(0, 0, birthTime, maxTime), new Vector4(moveDirection, r4), new Vector4(objectDirection, totalSize), new Vector4(particleSize, 0, 0, 0));
            }

            //Copy vertices to device buffer
            deviceVertexBuffer = new VertexBuffer(device, VertexExplosion.vertexDeclaration, explosionVertices.Length, BufferUsage.WriteOnly);
            deviceVertexBuffer.SetData(explosionVertices);
        }


        public void Draw(GameTime gametime)
        {
            //ZBuffer leeren
           // device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

            if (explosionVertices != null)
            {




                expEffect.CurrentTechnique = expEffect.Techniques["Explosion"];
                expEffect.Parameters["xWorld"].SetValue(Matrix.Identity);
                expEffect.Parameters["xView"].SetValue(view);
                expEffect.Parameters["xProjection"].SetValue(projection);
                expEffect.Parameters["xCamPos"].SetValue(camPos);
                expEffect.Parameters["xCamUp"].SetValue(camUp);
                expEffect.Parameters["xTime"].SetValue((float)gametime.TotalGameTime.TotalMilliseconds);
                expEffect.Parameters["xExplosionTexture"].SetValue(expTexture);



                foreach (EffectPass pass in expEffect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                }

                //bei rauch alpha blending
                device.BlendState = BlendState.Additive;
                device.DepthStencilState = DepthStencilState.None;

                device.DrawUserPrimitives<VertexExplosion>(PrimitiveType.TriangleList, explosionVertices, 0, explosionVertices.Length / 3, VertexExplosion.vertexDeclaration);

                device.DepthStencilState = DepthStencilState.Default;
                device.BlendState = BlendState.Opaque;
            }

        }

        private bool HasFinished(GameTime gameTime)
        {
            float currentTime = (float)gameTime.TotalGameTime.TotalMilliseconds;

            if ((currentTime - birthTime) > maxTime + 500.0f )
            {
                return true;
            }

            return false;
        }

        public void UpdateExplosion(GameTime gameTime)
        {
            if (HasFinished(gameTime))
            {
                explosionVertices = null;
            }
        }

    }
}
